package org.study.lucene.api.util;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 类的描述
 *
 * @author doveylovey
 * @email 1135782208@qq.com
 * @date 2021年12月28日
 */
public class IdWorker {
    private long workerId;
    private long datacenterId;
    private long sequence;
    private long twepoch = 1640703323042L;
    private long workerIdBits = 5L;
    private long datacenterIdBits = 5L;
    private long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    private long sequenceBits = 12L;
    private long workerIdShift = sequenceBits;
    private long datacenterIdShift = sequenceBits + workerIdBits;
    private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    private long sequenceMask = -1L ^ (-1L << sequenceBits);
    private long lastTimestamp = -1L;

    public IdWorker(long workerId, long datacenterId, long sequence) {
        if (workerId > maxWorkerId || workerId < 0) {
            String exMsg = String.format("workerId 不能大于 %d 或小于 0", maxWorkerId);
            throw new IllegalArgumentException(exMsg);
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            String exMsg = String.format("datacenterId 不能大于 %d 或小于 0", maxDatacenterId);
            throw new IllegalArgumentException(exMsg);
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
        this.sequence = sequence;
    }

    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();
        if (timestamp < lastTimestamp) {
            System.err.printf("clock is moving backwards.  Rejecting requests until %d.", lastTimestamp);
            throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = this.tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0;
        }
        lastTimestamp = timestamp;
        return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;
    }

    private long tilNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    public static void main(String[] args) {
        System.out.println(System.currentTimeMillis());
        Date date = new Date(1640703323042L);
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(format.format(date));
        IdWorker worker = new IdWorker(1, 1, 1);
        for (int i = 0; i < 100; i++) {
            System.out.println(worker.nextId());
        }
    }
}
